package com.zw.cloud.xxljob.utils;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSONObject;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.glue.GlueTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.net.HttpCookie;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * xxl-job 客户端向服务端通过接口添加任务
 */
@Slf4j
@Component
public class XXLJobUtils {
    @Value("${xxl.job.admin.addresses}")
    private String adminAddress;
    @Value("${xxl.job.accessToken}")
    private String accessToken;
    @Value("${xxl.job.groupId}")
    private String jobGroup;
    @Value("${xxl.job.userName}")
    private String userName;
    @Value("${xxl.job.password}")
    private String password;
    @Value("${xxl.job.alarmEmail}")
    private String alarmEmail;
    private final Map<String, String> loginCookie = new HashMap<>();

    private final String cookieKey = "XXL_JOB_LOGIN_IDENTITY";

    /**
     * 登录xxl平台 获得会话cookie
     * 先从内存缓存中获取，若无 则走登录
     *
     * @return 会话cookie
     */
    public String login() {
        if (loginCookie.containsKey(cookieKey)) {
            return loginCookie.get(cookieKey);
        }
        String url = adminAddress + "/login";
        HttpResponse response = HttpRequest.post(url)
                .form("userName", userName)
                .form("password", password)
                .execute();
        List<HttpCookie> cookies = response.getCookies();
        Optional<HttpCookie> cookieOpt = cookies.stream()
                .filter(cookie -> cookie.getName().equals(cookieKey)).findFirst();
        if (cookieOpt.isEmpty()) {
            throw new RuntimeException("get xxl-job cookie error!");
        }

        String value = cookieOpt.get().getValue();
        loginCookie.put(cookieKey, value);
        return value;
    }

    /**
     * 添加定时任务
     *
     * @param param  json字符串
     * @param delaySeconds 延时second秒后执行
     */
    public void addJob(String executorHandlerName,JSONObject param, long delaySeconds,String jobDesc) {
        log.info("[XXLJobUtils][addJob]创建xxl-job任务param is {},delaySeconds is {}", param.toJSONString(), delaySeconds);

        String response = handleAddJob(executorHandlerName,param, delaySeconds,jobDesc);
        JSON json = JSONUtil.parse(response);
        Object code = json.getByPath("code");
        if (Objects.equals(code, ReturnT.SUCCESS_CODE)) {
            log.info("[XXLJobUtils][addJob] 定时任务添加成功,param is {},response is {}", param.toJSONString(),response);
        } else {
            //重新登陆重试
            loginCookie.clear();
            response = handleAddJob(executorHandlerName,param, delaySeconds,jobDesc);
            json = JSONUtil.parse(response);
            code = json.getByPath("code");
            if (Objects.equals(code, ReturnT.SUCCESS_CODE)) {
                log.info("[XXLJobUtils][addJob] 定时任务添加成功,param is {},response is {}", param.toJSONString(),response);
            } else {
                log.error("[XXLJobUtils][addJob] 定时任务添加失败: {}", response);
            }
        }
    }

    /**
     * 处理添加任务的核心逻辑
     *
     * @param param  json字符串
     * @param delaySeconds 延时second秒后执行
     * @return xxljob返回体
     */
    private String handleAddJob(String executorHandlerName,JSONObject param, long delaySeconds,String jobDesc) {
        String cookie = login();

        // 构建请求参数
        String cron = getCronExpressionFromDelaySeconds(delaySeconds);
        Map<String, Object> requestParam = new HashMap<>();
        requestParam.put("jobGroup", jobGroup); // 执行器注册的分组ID
        requestParam.put("jobDesc", jobDesc); // 任务描述
        requestParam.put("author", "System"); // 任务创建者
        requestParam.put("executorRouteStrategy", "ROUND"); // 执行器路由策略 轮询
        requestParam.put("executorHandler", executorHandlerName); // 执行器任务处理器
        // SERIAL_EXECUTION:单机串行 DISCARD_LATER:丢弃后续调度
        requestParam.put("executorBlockStrategy", "SERIAL_EXECUTION"); // 阻塞处理策略
        requestParam.put("executorParam", param);
        requestParam.put("glueType", GlueTypeEnum.BEAN.getDesc()); // 任务模式为 BEAN 模式
        requestParam.put("executorTimeout", 0); // 执行器超时时间，单位为秒，0 表示不超时
        requestParam.put("executorFailRetryCount", 0); // 失败重试次数
        requestParam.put("scheduleType", "CRON");
        requestParam.put("scheduleConf", cron);
        requestParam.put("cronGen_display", cron);
        requestParam.put("alarmEmail", alarmEmail);
        requestParam.put("glueRemark", "GLUE代码初始化");
        requestParam.put("schedule_conf_CRON", cron);
        requestParam.put("schedule_conf_FIX_RATE", "");
        requestParam.put("schedule_conf_FIX_DELAY", "");
        requestParam.put("childJobId", "");
        requestParam.put("glueSource", "");
        requestParam.put("misfireStrategy", "DO_NOTHING");
        requestParam.put("triggerStatus", 1);//直接开始运行
        // 构建调度器地址
        String triggerAddress = adminAddress + "/jobinfo/add";

        // 解析返回结果
        HttpResponse response = HttpRequest.post(triggerAddress)
                .form(requestParam)
                .cookie(cookieKey + "=" + cookie)
                .header("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
                .header("X-Requested-With", "XMLHttpRequest")
                .header("XXL_JOB_ACCESS_TOKEN", accessToken)
                .execute();

        return response.body();
    }


    /**
     * 删除定时任务
     *
     * @param jobId 需要删除的定时任务id
     */
    public void deleteJob(String jobId) {
        log.info("[XXLJobUtils][deleteJob]删除定时任务，jobId is {}", jobId);
        String response = handleDeleteJob(jobId);
        JSON json = JSONUtil.parse(response);
        Object code = json.getByPath("code");
        if (Objects.equals(code, ReturnT.SUCCESS_CODE)) {
            log.info("[XXLJobUtils][deleteJob]定时任务{}删除成功", jobId);
        } else {
            //重新登陆重试
            loginCookie.clear();
            response = handleDeleteJob(jobId);
            json = JSONUtil.parse(response);
            code = json.getByPath("code");
            if (Objects.equals(code, ReturnT.SUCCESS_CODE)) {
                log.info("[XXLJobUtils][deleteJob]定时任务{}删除成功", jobId);
            } else {
                log.error("[XXLJobUtils][deleteJob]定时任务添加失败: {}", response);
            }
        }
    }

    /**
     * 删除定时任务的核心处理逻辑
     *
     * @param jobId 需要删除的任务id
     * @return xxljob返回体
     */
    private String handleDeleteJob(String jobId) {
        String cookie = login();
        // 构建删除任务的地址
        String removeAddress = adminAddress + "/jobinfo/remove";
        // 构建请求参数
        Map<String, Object> requestParam = new HashMap<>();
        requestParam.put("jobGroup", jobGroup);
        requestParam.put("id", jobId);
        // 解析返回结果
        HttpResponse response = HttpRequest.post(removeAddress)
                .form(requestParam)
                .cookie(cookieKey + "=" + cookie)
                .header("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
                .header("X-Requested-With", "XMLHttpRequest")
                .header("XXL_JOB_ACCESS_TOKEN", accessToken)
                .execute();
        return response.body();
    }


    /**
     * 获取当前时间之后x秒的cron表达式
     * @param delaySeconds 当前时间往后延迟几秒
     * @return cron表达式
     */
    public static String getCronExpressionFromDelaySeconds(long delaySeconds) {
        LocalDateTime localDateTime = LocalDateTime.now().plusSeconds(delaySeconds);
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("ss mm HH dd MM ? yyyy");
        return localDateTime.format(dateTimeFormatter);
    }
}
